/*
 * Copyright (c) 2024.  little3201.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *       https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.server.starter.exploiter.controller;

import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.server.starter.exploiter.dto.TemplateDTO;
import com.server.starter.exploiter.service.TemplateService;
import com.server.starter.exploiter.vo.TemplateVO;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@RestController
@RequestMapping("/templates")
public class TemplateController {

    private final Logger logger = LoggerFactory.getLogger(TemplateController.class);

    private final TemplateService templateService;

    /**
     * Constructor for Controller.
     *
     * @param templateService a {@link TemplateService} object
     */
    public TemplateController(TemplateService templateService) {
        this.templateService = templateService;
    }

    /**
     * Retrieves a paginated list of records.
     *
     * @param page       The page number.
     * @param size       The number of records per page.
     * @param sortBy     The field to sort by.
     * @param descending Whether sorting should be in descending order.
     * @param name       The name filter for the schemas.
     * @return A paginated list of schemas, or 204 status code if an error occurs.
     */
    @PreAuthorize("hasAuthority('SCOPE_templates:read')")
    @GetMapping
    public ResponseEntity<Page<TemplateVO>> retrieve(@RequestParam int page, @RequestParam int size,
                                                     String sortBy, boolean descending, String name) {
        Page<TemplateVO> voPage;
        try {
            voPage = templateService.retrieve(page, size, sortBy, descending, name);
        } catch (Exception e) {
            logger.error("Retrieve template occurred an error: ", e);
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.ok(voPage);
    }

    /**
     * Fetches a record by ID.
     *
     * @param id The record ID.
     * @return The record data, or 204 status code if an error occurs.
     */
    @PreAuthorize("hasAuthority('SCOPE_templates:read')")
    @GetMapping("/{id}")
    public ResponseEntity<TemplateVO> fetch(@PathVariable Long id) {
        TemplateVO vo;
        try {
            vo = templateService.fetch(id);
        } catch (Exception e) {
            logger.error("Fetch template occurred an error: ", e);
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.ok(vo);
    }

    /**
     * Checks if a record exists by name, suffix, version.
     *
     * @param name    The record name.
     * @param suffix  The record suffix.
     * @param version The record version.
     * @param id      The record ID.
     * @return True if the record exists, or 204 status code if an error occurs.
     */
    @PreAuthorize("hasAuthority('SCOPE_templates:read')")
    @GetMapping("/exists")
    public ResponseEntity<Boolean> exists(@RequestParam String name, @RequestParam String suffix,
                                          @RequestParam String version, Long id) {
        boolean exists;
        try {
            exists = templateService.exists(name, suffix, version, id);
        } catch (Exception e) {
            logger.info("Query template exists occurred an error: ", e);
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.ok(exists);
    }

    /**
     * Creates a new record.
     *
     * @param dto The record data transfer object.
     * @return The created record, or 417 status code if an error occurs.
     */
    @PreAuthorize("hasAuthority('SCOPE_templates:write')")
    @PostMapping
    public ResponseEntity<TemplateVO> create(@RequestBody @Valid TemplateDTO dto) {
        TemplateVO vo;
        try {
            boolean existed = templateService.exists(dto.getName(), null);
            if (existed) {
                return ResponseEntity.status(HttpStatus.CONFLICT).build();
            }
            vo = templateService.create(dto);
        } catch (Exception e) {
            logger.error("Create template occurred an error: ", e);
            return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).build();
        }
        return ResponseEntity.status(HttpStatus.CREATED).body(vo);
    }

    /**
     * Modifies an existing record.
     *
     * @param id  The record ID.
     * @param dto The record data transfer object.
     * @return The modified record, or 417 status code if an error occurs.
     */
    @PreAuthorize("hasAuthority('SCOPE_templates:write')")
    @PutMapping("/{id}")
    public ResponseEntity<TemplateVO> modify(@PathVariable Long id, @RequestBody @Valid TemplateDTO dto) {
        TemplateVO vo;
        try {
            vo = templateService.modify(id, dto);
        } catch (Exception e) {
            logger.error("Modify template occurred an error: ", e);
            return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build();
        }
        return ResponseEntity.accepted().body(vo);
    }

    /**
     * Removes a record by ID.
     *
     * @param id The record ID.
     * @return 200 status code if successful, or 417 status code if an error occurs.
     */
    @PreAuthorize("hasAuthority('SCOPE_templates:write')")
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> remove(@PathVariable Long id) {
        try {
            templateService.remove(id);
        } catch (Exception e) {
            logger.error("Remove template occurred an error: ", e);
            return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).build();
        }
        return ResponseEntity.ok().build();
    }

    /**
     * Enable a record when enabled is false or disable when enabled is ture.
     *
     * @param id The record ID.
     * @return 200 status code if successful, or 417 status code if an error occurs.
     */
    @PreAuthorize("hasAuthority('SCOPE_templates:write')")
    @PatchMapping("/{id}")
    public ResponseEntity<Boolean> toggleStatus(@PathVariable Long id) {
        boolean enabled;
        try {
            enabled = templateService.enable(id);
        } catch (Exception e) {
            logger.error("Toggle enabled occurred an error: ", e);
            return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build();
        }
        return ResponseEntity.accepted().body(enabled);
    }

    /**
     * Import the records.
     *
     * @return 200 status code if successful, or 417 status code if an error occurs.
     */
    @PreAuthorize("hasAuthority('SCOPE_templates:import')")
    @PostMapping("/import")
    public ResponseEntity<List<TemplateVO>> importFromExcel(MultipartFile file) {
        List<TemplateVO> voList;
        try (ExcelReader reader = ExcelUtil.getReader(file.getInputStream())) {
            List<TemplateDTO> dtoList = reader.readAll(TemplateDTO.class);
            voList = templateService.createAll(dtoList);
        } catch (Exception e) {
            logger.error("Import template occurred an error: ", e);
            return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).build();
        }
        return ResponseEntity.ok().body(voList);
    }

    /**
     * Export the records.
     *
     * @return The list of records value objects, or 417 status code if an error occurs.
     */
    @PreAuthorize("hasAuthority('SCOPE_templates:export')")
    @GetMapping("/export")
    public ResponseEntity<Void> exportToExcel(@RequestParam(required = false) List<Long> ids, HttpServletResponse response) {
        try (ExcelWriter writer = ExcelUtil.getWriter(true);
             ServletOutputStream out = response.getOutputStream()) {
            List<TemplateVO> voList = templateService.retrieve(ids);

            writer.write(voList, true);
            //response为HttpServletResponse对象
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=data.xlsx");

            writer.flush(out, true);
        } catch (Exception e) {
            logger.error("Export template occurred an error: ", e);
            return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).build();
        }
        return ResponseEntity.ok().build();
    }

}
